package com.gc.cscott.cli;

import com.gc.cscott.domain.contest.Contest;
import com.gc.cscott.domain.contest.Task;
import com.gc.cscott.domain.contest.TestCase;
import com.gc.cscott.domain.contest.numbers.TaskNumber;
import com.gc.cscott.domain.contest.numbers.TestCaseNumber;
import com.gc.cscott.domain.Competitor;
import com.gc.cscott.domain.result.CompetitorResult;
import com.gc.cscott.domain.result.TestCaseResult;
import com.gc.cscott.fs.FileSystemWorker;
import com.gc.cscott.store.ResultsStorage;
import com.gc.cscott.util.CscottUtil;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;

/**
 *
 * @author root
 */
public class ReportResultsCommand implements Command {
    @Inject
    FileSystemWorker fsWorker;

    @Inject
    ResultsStorage resultsStorage;

    @Inject
    Contest contest;

    boolean modeDetails = false;

    String separator = " ";

    boolean modeScores = false;

    boolean modeSubtotals = false;

    boolean modeShowPassed = true;

    boolean modeShowFailed = true;

    String passedSign = "+";

    boolean modeNumbering = false;

    boolean modeNoHeader = false;

    boolean modeShortNames = false;

    boolean modeSplit = false;

    String likeFilter = "";

    boolean modeEditor = false;

    public List<String> listHelp() {
        return Arrays.asList(
                "Report competitors results.",
                "Optionals:",
                "  T1,3-4    - tasks filter.",
                "  details   - print d_etailed results for each task.",
                "  scores    - print s_cores in addition.",
                "  csv       - use ',' as delimiter.",
                "  csv_sc    - use ';' as delimiter.",
                "  subtotals - print subtotals for each task.",
                "  01        - use 0/1 markers for instead of +/-.",
                "  noheader  - don't pring header.",
                "  numbering - print orfinal numbers for competitors.",
                "  nms       - print only first 8 letters for names.",
                "  nofail    - do not print count of failed test cases.",
                "  nopass    - do not print count of passed cases.",
                "  compress  - do not use separators between test results.",
                "  split     - user separator '|' to split results.",
                "  like:<xx> - like filter for competitors name",
                "  edit      - will open report in default spreadsheet editor.");
    }

    public List<String> perform(List<String> pendingArgs) {
        final Set<TaskNumber> taskSet = new TreeSet<TaskNumber>();
        final LinkedList<String> mutableArgs = new LinkedList<String>(pendingArgs);
        while (mutableArgs.isEmpty() == false) {
            String first = mutableArgs.getFirst();
            List<TaskNumber> taskList = Parser.parseTaskList(first);
            if (taskList != null) {
                taskSet.addAll(taskList);
            } else if (first.equals("scores")) {
                modeScores = true;
            } else if (first.equals("details")) {
                modeDetails = true;
            } else if (first.equals("compress")) {
                separator = "";
            } else if (first.equals("csv")) {
                separator = ",";
            } else if (first.equals("csv_sc")) {
                separator = ";";
            } else if (first.equals("subtotals")) {
                modeSubtotals = true;
            } else if (first.equals("numbering")) {
                modeNumbering = true;
            } else if (first.equals("split")) {
                modeSplit = true;
            } else if (first.equals("nopass")) {
                modeShowPassed = false;
            } else if (first.equals("nofail")) {
                modeShowFailed = false;
            } else if (first.startsWith("like:")) {
                likeFilter = first.substring("like:".length());
            } else if (first.equals("edit")) {
                modeEditor = true;
            } else if (first.equals("01")) {
                passedSign="1";
            } else if (first.equals("noheader")) {
                modeNoHeader = true;
            } else if (first.equals("nms")) {
                modeShortNames = true;
            } else if (first.startsWith("-") == false) {
                throw new Error("Wrong arguments: " + mutableArgs);
            } else {
                break; //not matched, will out from the loop without removing token.
            }
            mutableArgs.removeFirst();
        }
        if (taskSet.isEmpty() == true) {
            taskSet.addAll(contest.getTaskNumbersAvailable());
        }


        try {
            final File reportFile;
            final PrintStream output;
            if (modeEditor) {
                reportFile = File.createTempFile("report-results-" + CscottUtil.currentTimeString() + "-", ".csv");
                output = new PrintStream(reportFile);
            } else {
                reportFile = null;
                output = System.out;
            }

            final Set<Competitor> competitorSet = new TreeSet<Competitor>(fsWorker.listCompetitorsAll());
            if (competitorSet.isEmpty() == true) {
                output.println("No competitors in list");
            } else {
                final int nameLengthLimit;
                if (modeShortNames) {
                    nameLengthLimit = 8;
                } else {
                    nameLengthLimit = Math.max(ReportUtils.longestName(competitorSet), 4);
                }

                if (!modeNoHeader) {
                    if (modeNumbering) {
                        output.print(" No"); //3 digits for numbers
                        output.print(separator);
                    }
                    output.printf("%-" + nameLengthLimit + "s", "name");
                    output.print(separator);
                    for (Task task : contest.getTasks(taskSet)) {
                        if (modeDetails) {
                            for (TestCase testCase : task.getTestCaseList()) {
                                if (modeScores) {
                                    output.printf("%2d", testCase.getNumber().intValue());
                                } else {
                                    output.print(testCase.getNumber().intValue() % 10);
                                }
                                output.print(separator);
                            }
                            if (modeSubtotals) {
                                if (modeShowPassed) {
                                    output.print("PS");
                                    output.print(separator);
                                }
                                if (modeShowFailed) {
                                    output.print("FL");
                                    output.print(separator);
                                }
                                if (modeScores) {
                                    output.print(String.format("%4s", "" + task.getNumber()));
                                    output.print(separator);
                                }
                                if (modeSplit) {
                                    output.print("|");
                                    output.print(separator);
                                }
                            }
                        }
                    }
                    if (modeShowPassed) {
                        output.print("PASS");
                        output.print(separator);
                    }
                    if (modeShowFailed) {
                        output.print("FAIL");
                        output.print(separator);
                    }
                    if (modeScores) {
                        output.print("Score");
                        output.print(separator);
                    }

                    output.println();
                }


                int index = 1;
                for (Competitor competitor : competitorSet) {
                    if (competitor.getName().contains(likeFilter) == false) {
                        continue;
                    }
                    //print competitor info.
                    if (modeNumbering) {
                        output.printf("%3d", index++);
                        output.print(separator);
                    }
                    output.printf("%-" + nameLengthLimit + "s",
                            StringUtils.substring(competitor.getName(), 0, nameLengthLimit));
                    output.print(separator);
                    //print test results.
                    CompetitorResult result = resultsStorage.loadResults(competitor);
                    int totalPassed = 0;
                    int totalFailed = 0;
                    int totalScore = 0;
                    for (TaskNumber taskNumber : taskSet) {
                        Task task = contest.getTask(taskNumber);
                        int taskPassed = 0;
                        int taskFailed = 0;
                        int taskScore = 0;
                        for (TestCase testCase : task.getTestCaseList()) {
                            TestCaseResult testCaseResult = result.getResult(task.getNumber(), testCase.getNumber());
                            if (testCaseResult != null) {
                                if (testCaseResult.getResult() == TestCaseResult.HowEnded.Correct) {
                                    taskPassed++;
                                    taskScore += testCase.getScore();
                                    if (modeDetails) {
                                        if (modeScores) {
                                            output.printf("%2d", testCase.getScore());
                                        } else {
                                            output.print(passedSign);
                                        }
                                    }

                                } else {
                                    taskFailed++;
                                    if (modeDetails) {
                                        if (modeScores) {
                                            output.print(testCaseResult.getResult().getCode());
                                        } else {
                                            output.print(testCaseResult.getResult().getCode().substring(0, 1));
                                        }
                                    }
                                }
                            } else {
                                if (modeDetails) {
                                    if (modeScores) {
                                        output.print("  ");
                                    } else {
                                        output.print(" ");
                                    }
                                }

                            }
                            if (modeDetails) {
                                output.print(separator);
                            }
                        }
                        if (modeSubtotals) {
                            if (modeShowPassed) {
                                output.printf("%2d", taskPassed);
                                output.print(separator);
                            }
                            if (modeShowFailed) {
                                output.printf("%2d", taskFailed);
                                output.print(separator);
                            }
                            if (modeScores) {
                                output.printf("%4d", taskScore);
                                output.print(separator);
                            }
                            if (modeSplit) {
                                output.print("|");
                                output.print(separator);
                            }
                        }
                        totalPassed += taskPassed;
                        totalFailed += taskFailed;
                        totalScore += taskScore;
                    }
                    if (modeShowPassed) {
                        output.printf("%4d", totalPassed);
                        output.print(separator);
                    }
                    if (modeShowFailed) {
                        output.printf("%4d", totalFailed);
                        output.print(separator);
                    }
                    if (modeScores) {
                        output.printf("%5d", totalScore);
                        output.print(separator);
                    }
                    output.println();
                }
            }
            if (reportFile!=null){
                output.close();
                Desktop.getDesktop().open(reportFile);
            }
            return ImmutableList.copyOf(mutableArgs);
        } catch (IOException ex) {
            throw new Error(ex);
        }
    }
}
